﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
using System.Security;
using System.Threading;
using AzukiMap.Tools;
using AzukiMap.Tools.Logger;
#if SILVERLIGHT
using System.Windows.Browser;
#else
using System.Web;
#endif


[assembly: InternalsVisibleTo("TestAzukiMapSL")]
namespace AzukiMap.Request
{
    public delegate void GetDataCompletedHandler(object sender, GetDataCompletedArgs e);

    public abstract class HttpGetRequest
    {
        protected string _uri;
        protected Dictionary<string, string> _requestParams;
        string _requestMethod;
        string _requestId;
        Object _attachedObject;
        SynchronizationContext _uiThread;

        public string Uri
        {
            get { return _uri; }
            set { _uri = value; }
        }

        public Dictionary<string, string> RequestParams
        {
            get { return _requestParams; }
            set { _requestParams = value; }
        }

        public string RequestMethod
        {
            get { return _requestMethod; }
            set { _requestMethod = value; }
        }

        public string RequestId
        {
            get { return _requestId; }
            set { _requestId = value; }
        }

        public Object AttachedObject
        {
            get { return _attachedObject; }
            set { _attachedObject = value; }
        }

        public event GetDataCompletedHandler GetDataCompleted;

        protected virtual void OnGetDataCompleted(GetDataCompletedArgs e)
        {
            if (GetDataCompleted != null)
            {
                GetDataCompleted(this, e);
            }
        }

        // Called from other thread by Post().
        public void OnGetDataCompleted(Object e)
        {
            OnGetDataCompleted((GetDataCompletedArgs)e);
        }

        public HttpGetRequest(string uri)
        {
            Logger.Debug(
                "HttpGetRequest.HttpGetRequest(1):"
                + "uri = " + uri
                );
            init(uri);
        }

        void init(string uri)
        {
            _uri = uri;
            _requestMethod = "GET";
            _requestId = "req" + Serial.GetSerialNumber().ToString();
            _requestParams = null;
            _attachedObject = null;
            _uiThread = SynchronizationContext.Current;

            Logger.Debug(
                "HttpGetRequest.Init():"
                + "uri = " + uri
                + "_requestId = " + _requestId
                );
        }

        public void Request()
        {
            Uri requestUri = null;
            if ( _requestMethod == "GET" ) {
                string requestParamStr = CreateRequestGetString();

                if (_uri.IndexOf('?') >= 0) requestParamStr = "&" + requestParamStr;

                try
                {
                    requestUri = new Uri(_uri + requestParamStr, UriKind.RelativeOrAbsolute);
                } catch
                {
                    Logger.Error("Request URI is not valid. " + _uri + requestParamStr);
                    return;
                }
            } else {
                throw new NotImplementedException();
            }

            Logger.Debug(
                "HttpGetRequest.Request():"
                + "requestUri = " + requestUri 
                );

            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(requestUri);
                RequestState state =
                    new RequestState(req, _requestId, _attachedObject);
                req.Method = _requestMethod;
                req.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
                
            }
            catch (NotSupportedException ex)
            {
                Logger.Fatal("NotSupportedException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
            catch (ProtocolViolationException ex)
            {
                Logger.Fatal("ProtocolViolationException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
            catch (WebException ex)
            {
                Logger.Fatal("WebException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
            catch (InvalidOperationException ex)
            {
                Logger.Fatal("InvalidOperationException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
            catch (SecurityException ex)
            {
                Logger.Fatal("SecurityException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }
            catch(ArgumentNullException ex)
            {
                Logger.Fatal("ArgumentNullException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                throw ex;
            }

        }

        void ResponseCallback(IAsyncResult asynRes)
        {
            HttpWebResponse res;
            RequestState state;
            HttpWebRequest req;
            try
            {
                state = (RequestState) asynRes.AsyncState;
                req = state.Req;
                res = (HttpWebResponse) req.EndGetResponse(asynRes);
            }
            catch (NotImplementedException ex)
            {
                Logger.Fatal("NotImplementedException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }
            catch (WebException ex)
            {
                Logger.Fatal("WebException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }
            catch (InvalidOperationException ex)
            {
                Logger.Fatal("InvalidOperationException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }
            catch (SecurityException ex)
            {
                Logger.Fatal("SecurityException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }
            catch (ArgumentException ex)
            {
                Logger.Fatal("ArgumentException was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }
            catch (Exception ex)
            {
                Logger.Fatal("Exception was occured in the request.\n requestId: "
                    + _requestId + "\n" + ex.Message + "\n" + ex.StackTrace);
                //throw ex;
                return;
            }

            Stream s = res.GetResponseStream();
            if (s == null)
            {
                Logger.Info(
                    "HttpGetRequest.ResponceCallBack(): NoResultException was occured in the request. requestId = "
                    + state.ReqId);
                throw new NoResultException("No results in the response.", req, state.ReqId, state.AttachedObject );
            }
             Logger.Debug(
                    "HttpGetRequest.ResponceCallBack(): " 
                    + " requestId = " + state.ReqId
#if SILVERLIGHT
                    + " streamLength = " + s.Length
#endif
                    );
            
            BinaryReader resReader = new BinaryReader(s);
            string reqId = state.ReqId;
            object attachedObject = state.AttachedObject;

            // call a GetDataCompleted event handler.
            if (GetDataCompleted != null)
            {
                GetDataCompletedArgs e = new GetDataCompletedArgs(reqId, resReader, attachedObject);

                // fire a event on UI thread.
                _uiThread.Post(OnGetDataCompleted, e);
            }
        }

        internal string CreateRequestGetString()
        {
            SetRequestParams();

            if (_requestParams == null)
            {
                return "";
            }
            string paramStr = "";
            bool isFirstParam = true;
            foreach (KeyValuePair<string, string> p in _requestParams)
            {
                if (isFirstParam)
                {
                    isFirstParam = false;
                }
                else
                {
                    paramStr += "&";
                }
                paramStr += HttpUtility.UrlEncode(p.Key) 
                    + "=" + HttpUtility.UrlEncode(p.Value);
            }
            return paramStr;
        }

        internal abstract void SetRequestParams();
    }

    public class RequestState
    {
        HttpWebRequest _req;
        string _reqId;
        Object _attachedObject;

        public HttpWebRequest Req
        {
            get { return _req; }
        }

        public string ReqId
        {
            get { return _reqId; }
        }

        public Object AttachedObject
        {
            get { return _attachedObject; }
        }

        public RequestState(HttpWebRequest req, string reqId, Object attachedObject)
        {
            _req = req;
            _reqId = reqId;
            _attachedObject = attachedObject;
        }
    }

    public class GetDataCompletedArgs : EventArgs
    {
        BinaryReader _dataStream;
        string _reqId;
        object _attachedObject;

        public BinaryReader DataStream
        {
            get { return _dataStream; }
        }

        public string ReqId
        {
            get { return _reqId; }
        }

        public object AttachedObject
        {
            get { return _attachedObject; }
        }

        public GetDataCompletedArgs(string reqId, BinaryReader dataStream, object attachedObject)
        {
            _reqId = reqId;
            _dataStream = dataStream;
            _attachedObject = attachedObject;
        }
    }

    public class NoResultException : Exception
    {
        HttpWebRequest _req;
        string _reqId;
        object _attachedObject;

        public NoResultException(string message, HttpWebRequest req, string reqId, object attachedObject)
            : base(message)
        {
            _req = req;
            _reqId = reqId;
            _attachedObject = attachedObject;
        }
    }
}
